package com.bolt.support.base.service;

import com.bolt.common.collection.IteratorUtil;
import com.bolt.common.convert.Convert;
import com.bolt.common.reflect.ReflectionUtil;
import com.bolt.convention.exception.ServiceException;
import com.bolt.support.query.toolkit.JPAUtil;
import com.bolt.support.spring.jpa.entity.BaseTreeEntity;
import com.bolt.support.ui.tree.model.TreeDataProvider;
import com.bolt.support.ui.tree.vo.UITreeNode;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.NoResultException;
import javax.persistence.Query;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by Administrator on 2020/6/19.
 */

public abstract class BaseTreeService<T extends BaseTreeEntity <ID, T>, ID extends Serializable> extends BaseService <T, ID> implements TreeDataProvider <ID> {


    private T queryEntity(ID id) {
        try{
            Query query = entityManager.createQuery("from " + entityName + " where id=:id");
            query.setParameter("id", id);
            return JPAUtil.initializeAndUnproxy((T) query.getSingleResult());
        }catch (NoResultException ex){
            return null;
        }

    }


    private long findChildrenSize(ID pid) {
        Query query = entityManager.createQuery("select count(id) from  " + entityName + " where pid=:pid");
        query.setParameter("pid", pid);
        return (Long) query.getSingleResult();
    }

    private List <T> getSuperior(T entity, List <T> entitys) {
        if (entity.getPid() == null) {
            entitys.addAll(findRoot());
            return entitys;
        }
        entitys.addAll(findChildren(entity.getPid()));
        return getSuperior(queryEntity((ID) entity.getPid()), entitys);
    }

    /**
     * 针对父级菜单排序号修改，将对其所有子项排序号进行修改
     */
    private void updateAllChildrenOrderRank(T self) {
        Integer orderNum = self.getOrderRank();
        Integer subOrderNum = (orderNum * 10);
        List <T> children = findChildren(self.getId());
        if (!IteratorUtil.isEmpty(children)) {
            for (T item : children) {
                subOrderNum++;
                item.setOrderRank(subOrderNum);
                updateAllChildrenOrderRank(item);
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public T save(T entity) {

        if (entity.getParent() == null) {
            entity.setParent(null);
            entity.setLevel(0);
            entity.setChildrenSize(0);
        }
        if (entity.getParent() != null && entity.getParent().getId() != null) {
            T parent = queryEntity(entity.getParent().getId());
            long size = findChildrenSize(parent.getId());
            parent.setChildrenSize((int) (size + 1));
            entity.setLevel(parent.getLevel() + 1);
            entity.setChildrenSize(0);

        }
        if (!entity.isNew()) {
            T self = queryEntity(entity.getId());
            if (self.getOrderRank() != entity.getOrderRank()) {
                self.setOrderRank(entity.getOrderRank());
                updateAllChildrenOrderRank(self);
            }
            return this.update(entity, self, new String[]{"children", "childrenSize", "level", "pid"});
        }
        return getRepository().save(entity);
    }

    /**
     * 查询所有父级节点
     *
     * @return
     */
    public List <T> findRoot() {
        Query query = entityManager.createQuery("FROM " + entityName + " WHERE parent is null order by orderRank");
        return (List <T>) query.getResultList();
    }

    /**
     * 查询相关父级的每一层子级
     *
     * @param node
     * @return
     */
    public List <T> findChildren(T node) {
        Query query = entityManager.createQuery("FROM " + entityName + " WHERE parent.id = :parentId order by orderRank");
        query.setParameter("parentId", node.getId());
        return query.getResultList();
    }

    public List <T> findChildren(Serializable parentId) {
        Query query = entityManager.createQuery("FROM " + entityName + " WHERE parent.id = :parentId order by orderRank");
        query.setParameter("parentId", parentId);
        return query.getResultList();
    }

    /**
     * 用于异步获取树形数据
     *
     * @param parentId
     * @param params
     * @return
     */
    @Override
    public List <UITreeNode> getChildren(ID parentId, Map <String, String> params) {
        List <UITreeNode> children = new ArrayList <>();
        try {
            List <T> entities;
            if (parentId == null || parentId.equals(0L)) {
                entities = this.findRoot();
            } else {
                entities = findChildren(parentId);
            }
            for (T entity : entities) {
                UITreeNode treeNode = new UITreeNode();
                converterTreeNode(entity, treeNode);
                children.add(treeNode);
            }
        } catch (Exception ex) {
            logger.error(ex.toString());
            throw new ServiceException(ex);

        }
        return children;
    }

    public List <UITreeNode> buildTree(List <T> entities) {
        List <UITreeNode> trees = new ArrayList <>();
        Set <String> ids = new HashSet <>();
        for (T entity : entities) {
            UITreeNode treeNode = new UITreeNode();
            converterTreeNode(entity, treeNode);
            trees.add(treeNode);
        }
        for (UITreeNode menuDTO : trees) {
            for (UITreeNode it : trees) {
                if (it.getPId() != null && it.getPId().equals(menuDTO.getId())) {
                    if (menuDTO.getChildren() == null) {
                        menuDTO.setChildren(new ArrayList <>());
                    }
                    menuDTO.getChildren().add(it);
                    ids.add(it.getId());
                }
            }
        }
        trees = trees.stream().filter(s -> !ids.contains(s.getId())).collect(Collectors.toList());
        return trees;
    }

    /**
     * 处理扩展参转换
     *
     * @param entity
     */
    protected void converterTreeNode(T entity, UITreeNode treeNode) {
        treeNode.setId(String.valueOf(entity.getId()));
        treeNode.setParent(entity.hasChildren());
        treeNode.setLeaf(entity.leaf());
        treeNode.setPId(entity.getPid() != null ? String.valueOf(entity.getPid()) : null);
    }

    /**
     * 树级删除
     * @param entity 待操作数据
     */
    @Transactional(rollbackFor = Exception.class)
    public void delete(T entity) {
        //删除子节点是对应修改上级节点子数信息
        if (entity.getParent() != null && entity.getParent() != null) {
            T parent = queryEntity(entity.getParent().getId());
            if(parent != null){
                parent.setChildrenSize(parent.getChildrenSize() - 1);
            }
        }
        super.delete(entity);
    }

    /**
     * 查询所有子级包含本级及所有上级
     *
     * @param ids
     * @return
     */
    public List <T> findAll(List <ID> ids) {
        List <T> entities = new ArrayList <>();
        for (ID id : ids) {
            T entity = queryEntity(id);
            List <T> sub = getSuperior(entity, new ArrayList <>());
            entities.addAll(sub);
        }
        return entities;
    }

    public List <T> findAllChildren(List <T> menuList, List <T> allMenus) {
        for (T treeData : menuList) {
            allMenus.add(treeData);
            List <T> menus = findChildren(treeData.getId());
            if (menus != null && menus.size() != 0) {
                findAllChildren(menus, allMenus);
            }
        }
        return allMenus;
    }

    protected String nextCode(T entity, String fieldName) {
        int maxId = 0;
        if (null != entity.getParent()) {

            maxId = Convert.toInt(entityManager.createQuery("select max(" + fieldName + ") from " + entityName + " where parent=:parent")
                    .setParameter("parent", entity.getParent()).getSingleResult(), 0);
        } else {
            maxId = Convert.toInt(entityManager.createQuery("select max(" + fieldName + ") from " + entityName + " where parent  IS NULL")
                    .getSingleResult(), 0);
        }
        String newId = null;
        if (maxId == 0 && null == entity.getParent()) {
            newId = "10";
            return newId;
        }
        if (maxId == 0 && null != entity.getParent()) {
            T parent = queryEntity(entity.getParent().getId());
            String parentCode = (String) ReflectionUtil.getFieldValue(parent, fieldName);
            newId = parentCode + "01";
            return newId;
        }
        if (maxId != 0 && null != entity.getParent()) {
            newId = String.valueOf(maxId + 1);
            return newId;
        }
        return String.valueOf(maxId + 1);

    }
}

 